﻿// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX - License - Identifier: Apache - 2.0

namespace MidLevelQueryAndScanExample
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using Amazon.DynamoDBv2;
  using Amazon.DynamoDBv2.DocumentModel;

  // snippet-start:[dynamodb.dotnet35.MidLevelQueryAndScan]

  /// <summary>
  /// Shows how to perform mid level query procedures on an Amazon DynamoDB
  /// table. The example was created using the AWS SDK for .NET version 3.7 and
  /// .NET Core 5.0.
  /// </summary>
  public class MidLevelQueryAndScan
  {
    public static async Task Main()
    {
      IAmazonDynamoDB client = new AmazonDynamoDBClient();

      // Query examples.
      Table replyTable = Table.LoadTable(client, "Reply");
      string forumName = "Amazon DynamoDB";
      string threadSubject = "DynamoDB Thread 2";

      await FindRepliesInLast15Days(replyTable, forumName, threadSubject);
      await FindRepliesInLast15DaysWithConfig(replyTable, forumName, threadSubject);
      await FindRepliesPostedWithinTimePeriod(replyTable, forumName, threadSubject);

      // Get Example.
      Table productCatalogTable = Table.LoadTable(client, "ProductCatalog");
      int productId = 101;

      await GetProduct(productCatalogTable, productId);
    }

    /// <summary>
    /// Retrieves information about a product from the DynamoDB table
    /// ProductCatalog based on the product ID and displays the information
    /// on the console.
    /// </summary>
    /// <param name="tableName">The name of the table from which to retrieve
    /// product information.</param>
    /// <param name="productId">The ID of the product to retrieve.</param>
    public static async Task GetProduct(Table tableName, int productId)
    {
      Console.WriteLine("*** Executing GetProduct() ***");
      Document productDocument = await tableName.GetItemAsync(productId);
      if (productDocument != null)
      {
        PrintDocument(productDocument);
      }
      else
      {
        Console.WriteLine("Error: product " + productId + " does not exist");
      }
    }

    /// <summary>
    /// Retrieves replies from the passed DynamoDB table object.
    /// </summary>
    /// <param name="table">The table we want to query.</param>
    /// <param name="forumName">The name of the forum we are interested in.</param>
    /// <param name="threadSubject">The subject of the thread for which we are
    /// looking for replies.</param>
    public static async Task FindRepliesInLast15Days(
      Table table,
      string forumName,
      string threadSubject)
    {
      DateTime twoWeeksAgoDate = DateTime.UtcNow - TimeSpan.FromDays(15);
      QueryFilter filter = new QueryFilter("Id", QueryOperator.Equal, "Id");
      filter.AddCondition("ReplyDateTime", QueryOperator.GreaterThan, twoWeeksAgoDate);

      // Use Query overloads that takes the minimum required query parameters.
      Search search = table.Query(filter);

      do
      {
        var documentSet = await search.GetNextSetAsync();
        Console.WriteLine("\nFindRepliesInLast15Days: printing ............");

        foreach (var document in documentSet)
        {
          PrintDocument(document);
        }
      } while (!search.IsDone);
    }

    /// <summary>
    /// Retrieve replies made during a specific time period.
    /// </summary>
    /// <param name="table">The table we want to query.</param>
    /// <param name="forumName">The name of the forum we are interested in.</param>
    /// <param name="threadSubject">The subject of the thread which we are
    /// searching for replies.</param>
    public static async Task FindRepliesPostedWithinTimePeriod(
      Table table,
      string forumName,
      string threadSubject)
    {
      DateTime startDate = DateTime.UtcNow.Subtract(new TimeSpan(21, 0, 0, 0));
      DateTime endDate = DateTime.UtcNow.Subtract(new TimeSpan(1, 0, 0, 0));

      QueryFilter filter = new QueryFilter("Id", QueryOperator.Equal, forumName + "#" + threadSubject);
      filter.AddCondition("ReplyDateTime", QueryOperator.Between, startDate, endDate);

      QueryOperationConfig config = new QueryOperationConfig()
      {
        Limit = 2, // 2 items/page.
        Select = SelectValues.SpecificAttributes,
        AttributesToGet = new List<string>
        {
          "Message",
          "ReplyDateTime",
          "PostedBy",
        },
        ConsistentRead = true,
        Filter = filter,
      };

      Search search = table.Query(config);

      do
      {
        var documentList = await search.GetNextSetAsync();
        Console.WriteLine("\nFindRepliesPostedWithinTimePeriod: printing replies posted within dates: {0} and {1} ............", startDate, endDate);

        foreach (var document in documentList)
        {
          PrintDocument(document);
        }

      } while (!search.IsDone);
    }

    /// <summary>
    /// Perform a query for replies made in the last 15 days using a DynamoDB
    /// QueryOperationConfig object.
    /// </summary>
    /// <param name="table">The table we want to query.</param>
    /// <param name="forumName">The name of the forum we are interested in.</param>
    /// <param name="threadName">The bane of the thread which we are looking
    /// for replies.</param>
    public static async Task FindRepliesInLast15DaysWithConfig(
      Table table,
      string forumName,
      string threadName)
    {
      DateTime twoWeeksAgoDate = DateTime.UtcNow - TimeSpan.FromDays(15);
      QueryFilter filter = new QueryFilter("Id", QueryOperator.Equal, forumName + "#" + threadName);
      filter.AddCondition("ReplyDateTime", QueryOperator.GreaterThan, twoWeeksAgoDate);

      QueryOperationConfig config = new QueryOperationConfig()
      {
        Filter = filter,
        // Optional parameters.
        Select = SelectValues.SpecificAttributes,
        AttributesToGet = new List<string>
        {
          "Message",
          "ReplyDateTime",
          "PostedBy",
        },
        ConsistentRead = true,
      };

      Search search = table.Query(config);

      do
      {
        var documentSet = await search.GetNextSetAsync();
        Console.WriteLine("\nFindRepliesInLast15DaysWithConfig: printing ............");

        foreach (var document in documentSet)
        {
          PrintDocument(document);
        }
      } while (!search.IsDone);
    }

    /// <summary>
    /// Displays the contents of the passed DynamoDB document on the console.
    /// </summary>
    /// <param name="document">A DynamoDB document to display.</param>
    public static void PrintDocument(Document document)
    {
      Console.WriteLine();
      foreach (var attribute in document.GetAttributeNames())
      {
        string stringValue = null;
        var value = document[attribute];

        if (value is Primitive)
        {
          stringValue = value.AsPrimitive().Value.ToString();
        }
        else if (value is PrimitiveList)
        {
          stringValue = string.Join(",", (from primitive
            in value.AsPrimitiveList().Entries
            select primitive.Value).ToArray());
        }

        Console.WriteLine($"{attribute} - {stringValue}");
      }
    }
  }

  // snippet-end:[dynamodb.dotnet35.MidLevelQueryAndScan]
}
